import Vue from 'vue'
import axios from 'axios'
import store from '@/store'
import notification from 'ant-design-vue/es/notification'
import {
  VueAxios
} from './axios'
import {
  ACCESS_TOKEN
} from '@/store/mutation-types'
import apiConfig from '../config/api.config'
import {
  i18nRender
} from '@/locales';

const {
  httpProtocol,
  host,
  port,
  prefix
} = apiConfig

// 请求列表
const requestList = []

// token 是否失效
let invalideToken = false

// 取消列表
const CancelToken = axios.CancelToken
window.cancelAjaxMap = {};

const sources = {}

// 约定的服务端特殊状态码
const customCode = {
  20001: '字段重复或错误'
}

// 创建 axios 实例
const service = axios.create({
  baseURL: `${httpProtocol}${host}${port ? `:${port}` : ''}${prefix}`,
  timeout: 180000
})

/**
 * 将一个请求转换成对应的字符串，用于比较多个请求是否相同，这里下定义：
 * 当两个请求的method、完整url、全部参数均相同时，则认为这两个请求为“相同请求”
 * @param {*} config
 */
const genUniqueRequest = config => {
  let argStr = ''
  if (!!config && config.hasOwnProperty('params')) {
    argStr = JSON.stringify(config.params)
  } else if (!!config && config.hasOwnProperty('data')) {
    if (config.data instanceof FormData) {
      // 如果是formData则需要这样处理
      argStr = JSON.stringify(Object.fromEntries(config.data))
    } else {
      argStr = JSON.stringify(config.data)
    }
  }
  if (config) {
    return `${config.method}_${config.baseURL}/${config.url}_${argStr}`
  }
  return `_/_${argStr}`
}

/**
 * 将请求添加至列表
 * @param {*} config
 */
const addRequest = config => {
  // console.log(config)
  // 这里给“相同请求”下定义：method相同并且完整url相同
  const request = genUniqueRequest(config)
  config.cancelToken = new CancelToken(cancel => {
    sources[request] = cancel
    window.cancelAjaxMap[request] = cancel
  })
  // 1.判断请求是否已存在请求列表，避免重复请求，将当前请求添加进请求列表数组；
  if (requestList.includes(request)) {
    sources[request]('取消重复请求')
  } else {
    requestList.push(request)
    // 2. 如果有全局loading状态，在这里置为true
    // todo
  }
}

/**
 * 从列表中移除请求
 * @param {*} config
 */
const deleteRequest = config => {
  // 1.将当前请求中请求列表中删除
  const request = genUniqueRequest(config)
  requestList.splice(
    requestList.findIndex(item => item === request),
    1
  )
  delete window.cancelAjaxMap[request]
  // 2.当请求列表为空时，如果有全局loading状态，在这里置为false
  if (requestList.length === 0) {
    // todo
  }
}

const err = error => {
  if (invalideToken) return

  if (error.response) {
    const data = error.response.data
    const token = Vue.ls.get(ACCESS_TOKEN)
    if (error.response.status === 403) {
      notification.error({
        message: 'Forbidden',
        description: data.message
      })
      deleteRequest(error.config)
    }
    if (error.response.status === 401) {
      if (error.response.config.headers && error.response.config.headers.auth) {
        notification.error({
          message: '该APP没有数据授权！',
          description: '请到平台管理-应用管理页面编辑对应APP的平台基础数据配置！'
        })
        deleteRequest(error.config)
      } else {
        invalideToken = true
        notification.error({
          message: '错误',
          description: '登录信息失效，请重新登录！'
        })
        deleteRequest(error.config)
        if (token) {
          Vue.ls.remove(ACCESS_TOKEN);
          setTimeout(() => {
            window.location.href = `${window.location.origin}/#/login`;
          }, 1500);
        };
      }
    }
  } else if (error.code === 'ECONNABORTED') {
    notification.error({
      message: '错误',
      description: '连接超时，请稍后再试'
    })
    deleteRequest(error.config)
  } else if (axios.isCancel(error)) {
    if (error.message.config) {
      deleteRequest(error.message.config)
    }
    error.selfCancel = true;
    // 不包含取消请求
    // notification.error({
    //   message: '错误',
    //   description: error.message
    // })
    error.config && deleteRequest(error.config)
  }
  return Promise.reject(error)
}

// request interceptor
service.interceptors.request.use(config => {
  addRequest(config)
  const token = Vue.ls.get(ACCESS_TOKEN)
  if (token) {
    config.headers['Access-Token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
  }
  return config
}, err)

// response interceptor
service.interceptors.response.use(response => {
  deleteRequest(response.config)

  const {
    data
  } = response
  const {
    headers,
    responseType
  } = response.config
  // 文件下载
  if (responseType === 'arraybuffer') {
    return data
  } else if (!!headers && headers.validate) {
    // input校验需要ui渲染
    return data;
  } else if (['unique_error_code', 'iam.1021', 'iam.1024', 'ok', 'sso.ok', 200, 20001, '20001', 0].includes(data.code)) {
    return data;
  } else {
    if (!headers.hideNotification) {
      const errorMsg = data.message ? i18nRender(data.message) : i18nRender('common.serverError');
      notification.error({
        message: i18nRender('common.error'),
        description: errorMsg,
      });
    }
    return Promise.reject(data);
  }
}, err)

const installer = {
  vm: {},
  install(Vue) {
    Vue.use(VueAxios, service)
  }
}

export {
  installer as VueAxios, service as axios, genUniqueRequest
}